﻿// ------------------------------------------------------------------------------
// <copyright file="DatePicker.cs" company="skumaronline.com">
//    Copyright (C) 2012 Suresh Kumar T.
//
//    This program is free software; you can redistribute it and/or
//    modify it under the terms of the GNU General Public License
//    as published by the Free Software Foundation version 2
//    of the License.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    http://www.gnu.org/licenses/gpl-2.0.html
//
// </copyright>
// ------------------------------------------------------------------------------

namespace StoreAppLib.Controls
{
    using System;
    using System.Windows.Input;
    using Windows.UI;
    using Windows.UI.Core;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;

    /// <summary>
    /// DatePicker control class
    /// </summary>
    public sealed class DatePicker : TextBox
    {
        /// <summary>
        /// Date value dependency property
        /// </summary>
        public static readonly DependencyProperty DateProperty =
            DependencyProperty.Register("Date", typeof(DateTime), typeof(DatePicker), new PropertyMetadata(DateTime.MinValue, OnDatePropertyChanged));

        /// <summary>
        /// Register maximum date dependency property
        /// </summary>
        public static readonly DependencyProperty MaximumDateProperty =
            DependencyProperty.Register("MaximumDate", typeof(DateTime), typeof(DatePicker), new PropertyMetadata(DateTime.MaxValue));

        /// <summary>
        /// Register minimum date dependency property
        /// </summary>
        public static readonly DependencyProperty MinimumDateProperty =
            DependencyProperty.Register("MinimumDate", typeof(DateTime), typeof(DatePicker), new PropertyMetadata(DateTime.MinValue));

        /// <summary>
        /// Date format dependency property
        /// </summary>
        public static readonly DependencyProperty FormatProperty =
            DependencyProperty.Register("Format", typeof(string), typeof(DatePicker), new PropertyMetadata("dd-MMM-yyyy", OnFormatPropertyChanged));

        /// <summary>
        /// Date foreground color dependency property for days in current month
        /// </summary>
        public static readonly DependencyProperty DateForegroundProperty =
            DependencyProperty.Register("DateForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Colors.White)));

        /// <summary>
        /// Register disabled date foreground color dependency property
        /// </summary>
        public static readonly DependencyProperty DateDisabledForegroundProperty =
            DependencyProperty.Register("DateDisabledForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(100, 85, 85, 85))));

        /// <summary>
        /// Date foreground color dependency property for days not in current month
        /// </summary>
        public static readonly DependencyProperty DateOtherMonthForegroundProperty =
            DependencyProperty.Register("DateOtherMonthForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Colors.Gray)));

        /// <summary>
        /// Today date foreground color dependency property
        /// </summary>
        public static readonly DependencyProperty DateTodayForegroundProperty =
            DependencyProperty.Register("DateTodayForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Colors.RoyalBlue)));

        /// <summary>
        /// Date background color dependency property
        /// </summary>
        public static readonly DependencyProperty DateBackgroundProperty =
            DependencyProperty.Register("DateBackground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(100, 48, 47, 55))));

        /// <summary>
        /// Register disabled date background color dependency property
        /// </summary>
        public static readonly DependencyProperty DateDisabledBackgroundProperty =
            DependencyProperty.Register("DateDisabledBackground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(100, 28, 29, 33))));

        /// <summary>
        /// Selected date background color dependency property
        /// </summary>
        public static readonly DependencyProperty SelectedDateBackgroundProperty =
            DependencyProperty.Register("SelectedDateBackground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(100, 0, 129, 134))));

        /// <summary>
        /// Header foreground color dependency property for month, year and decade header
        /// </summary>
        public static readonly DependencyProperty HeaderForegroundProperty =
            DependencyProperty.Register("HeaderForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Colors.White)));

        /// <summary>
        /// Month header foreground color dependency property
        /// </summary>
        public static readonly DependencyProperty MonthHeaderForegroundProperty =
            DependencyProperty.Register("MonthHeaderForeground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Colors.White)));

        /// <summary>
        /// Month header background color dependency property
        /// </summary>
        public static readonly DependencyProperty MonthHeaderBackgroundProperty =
            DependencyProperty.Register("MonthHeaderBackground", typeof(SolidColorBrush), typeof(DatePicker), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(100, 0, 129, 134))));

        /// <summary>
        /// Show validation message dependency property
        /// </summary>
        public static readonly DependencyProperty ShowValidationMessageProperty =
            DependencyProperty.Register("ShowValidationMessage", typeof(bool), typeof(DatePicker), new PropertyMetadata(false));

        /// <summary>
        /// Selected date is less than From date message dependency property
        /// </summary>
        public static readonly DependencyProperty MinDateValidationMessageProperty =
            DependencyProperty.Register("MinDateValidationMessage", typeof(string), typeof(DatePicker), new PropertyMetadata("Selected date {0:dd MMMM yyyy} is invalid.\nPlease select a date between {1:dd MMMM yyyy} and {2:dd MMMM yyyy}."));

        /// <summary>
        /// Selected date is greater than To date message dependency property
        /// </summary>
        public static readonly DependencyProperty MaxDateValidationMessageProperty =
            DependencyProperty.Register("MaxDateValidationMessage", typeof(string), typeof(DatePicker), new PropertyMetadata("Selected date {0:dd MMMM yyyy} is invalid.\nPlease select a date between {1:dd MMMM yyyy} and {2:dd MMMM yyyy}."));

        /// <summary>
        /// Holds clear command
        /// </summary>
        private ICommand clearCommand;

        /// <summary>
        /// Initializes a new instance of the <see cref="DatePicker" /> class
        /// </summary>
        public DatePicker()
            : base()
        {
            base.IsReadOnly = true;
            this.clearCommand = new RelayCommand(this.Clear);
        }

        /// <summary>
        /// Event to handle date change
        /// </summary>
        public event DateTimeChangedEventHandler DateChanged;

        /// <summary>
        /// Gets or sets the date.
        /// </summary>
        /// <value>
        /// The date.
        /// </value>
        public DateTime Date
        {
            get
            {
                return (DateTime)this.GetValue(DateProperty);
            }

            set
            {
                this.SetValue(DateProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the minimum date.
        /// </summary>
        /// <value>
        /// The minimum date.
        /// </value>
        public DateTime MinimumDate
        {
            get
            {
                return (DateTime)this.GetValue(MinimumDateProperty);
            }

            set
            {
                this.SetValue(MinimumDateProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the maximum date.
        /// </summary>
        /// <value>
        /// The maximum date.
        /// </value>
        public DateTime MaximumDate
        {
            get
            {
                return (DateTime)this.GetValue(MaximumDateProperty);
            }

            set
            {
                this.SetValue(MaximumDateProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the date format.
        /// </summary>
        /// <value>
        /// The date.
        /// </value>
        public string Format
        {
            get
            {
                return (string)this.GetValue(FormatProperty);
            }

            set
            {
                this.SetValue(FormatProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the date foreground color for days in current month.
        /// </summary>
        /// <value>
        /// The date foreground color.
        /// </value>
        public SolidColorBrush DateForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateForegroundProperty);
            }

            set
            {
                this.SetValue(DateForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the disabled date foreground color.
        /// </summary>
        /// <value>
        /// The disabled date foreground color.
        /// </value>
        public SolidColorBrush DateDisabledForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateDisabledForegroundProperty);
            }

            set
            {
                this.SetValue(DateDisabledForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the date foreground color for days not in current month.
        /// </summary>
        /// <value>
        /// The other month date foreground color.
        /// </value>
        public SolidColorBrush DateOtherMonthForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateOtherMonthForegroundProperty);
            }

            set
            {
                this.SetValue(DateOtherMonthForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets today's foreground color.
        /// </summary>
        /// <value>
        /// The today's foreground color.
        /// </value>
        public SolidColorBrush DateTodayForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateTodayForegroundProperty);
            }

            set
            {
                this.SetValue(DateTodayForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets date background color.
        /// </summary>
        /// <value>
        /// The date background color.
        /// </value>
        public SolidColorBrush DateBackground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateBackgroundProperty);
            }

            set
            {
                this.SetValue(DateBackgroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the disabled date background color.
        /// </summary>
        /// <value>
        /// The disabled date background color.
        /// </value>
        public SolidColorBrush DateDisabledBackground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(DateDisabledBackgroundProperty);
            }

            set
            {
                this.SetValue(DateDisabledBackgroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets selected date background color.
        /// </summary>
        /// <value>
        /// The selected date background color.
        /// </value>
        public SolidColorBrush SelectedDateBackground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(SelectedDateBackgroundProperty);
            }

            set
            {
                this.SetValue(SelectedDateBackgroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the header foreground color for month, year and decade.
        /// </summary>
        /// <value>
        /// The header foreground color.
        /// </value>
        public SolidColorBrush HeaderForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(HeaderForegroundProperty);
            }

            set
            {
                this.SetValue(HeaderForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the month header foreground color.
        /// </summary>
        /// <value>
        /// The month header foreground color.
        /// </value>
        public SolidColorBrush MonthHeaderForeground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(MonthHeaderForegroundProperty);
            }

            set
            {
                this.SetValue(MonthHeaderForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the month header background color.
        /// </summary>
        /// <value>
        /// The month header background color.
        /// </value>
        public SolidColorBrush MonthHeaderBackground
        {
            get
            {
                return (SolidColorBrush)this.GetValue(MonthHeaderBackgroundProperty);
            }

            set
            {
                this.SetValue(MonthHeaderBackgroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to show validation message or not
        /// </summary>
        /// <value>
        /// True if validation message is to be showed else false.
        /// </value>
        public bool ShowValidationMessage
        {
            get
            {
                return (bool)this.GetValue(ShowValidationMessageProperty);
            }

            set
            {
                this.SetValue(ShowValidationMessageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the validation message when selected date is less than From date. Use .Net custom date format specifier to replace dates.
        /// 0=> Selected date
        /// 1=> Minimum date
        /// 2=> Maximum date
        /// </summary>
        /// <value>
        /// The validation message when selected date is less than From date
        /// </value>
        public string MinDateValidationMessage
        {
            get
            {
                return (string)this.GetValue(MinDateValidationMessageProperty);
            }

            set
            {
                this.SetValue(MinDateValidationMessageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the validation message when selected date is greater than To date. Use .Net custom date format specifier to replace dates.
        /// 0=> Selected date
        /// 1=> Minimum date
        /// 2=> Maximum date
        /// </summary>
        /// <value>
        /// The validation message when selected date is greater than To date
        /// </value>
        public string MaxDateValidationMessage
        {
            get
            {
                return (string)this.GetValue(MaxDateValidationMessageProperty);
            }

            set
            {
                this.SetValue(MaxDateValidationMessageProperty, value);
            }
        }

        /// <summary>
        /// Gets clear command
        /// </summary>
        public ICommand ClearCommand
        {
            get
            {
                return this.clearCommand;
            }
        }

        /// <summary>
        /// Gets a text value. Shadow base property to hide text, instead use Value property
        /// </summary>
        private new string Text
        {
            get { return string.Empty; }
        }

        /// <summary>
        /// Gets a value indicating whether control is readonly. Shadow base property to show always in readonly mode
        /// </summary>
        private new bool IsReadOnly
        {
            get { return true; }
        }

        /// <summary>
        /// Clear data value by setting to DateTime.MinValue
        /// </summary>
        public void Clear()
        {
            this.Clear(this);
        }

        /// <summary>
        /// When user tap on the date picker, show the date control in a popup
        /// </summary>
        /// <param name="e">Routed event argument</param>
        protected async override void OnTapped(Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
            base.OnTapped(e);

            MessageDialog errorMessage = null;
            
            try
            {
                DateControl flyout = new DateControl(this);

                flyout.Open();

                flyout.DateTimeChanged += this.OnDateTimeChanged;
            }
            catch (ArgumentException ex)
            {
                errorMessage = new MessageDialog(ex.Message);
                errorMessage.DefaultCommandIndex = 1;
            }

            if (errorMessage != null)
            {
                await errorMessage.ShowAsync();
            }
        }

        /// <summary>
        /// Callback for date value change
        /// </summary>
        /// <param name="target">The target</param>
        /// <param name="args">Property changed callback event argument</param>
        private static void OnDatePropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
        {
            DatePicker datePicker = target as DatePicker;

            if (datePicker != null && args.NewValue != args.OldValue)
            {
                datePicker.UpdateText();
            }
        }

        /// <summary>
        /// Callback for format value change
        /// </summary>
        /// <param name="target">The target</param>
        /// <param name="args">Property changed callback event argument</param>
        private static void OnFormatPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
        {
            DatePicker datePicker = target as DatePicker;

            if (datePicker != null && args.NewValue != args.OldValue)
            {
                datePicker.UpdateText();
            }
        }

        /// <summary>
        /// Occurs when user selects a date in the date control
        /// </summary>
        /// <param name="sender">The source</param>
        /// <param name="args">Date time change argument</param>
        private void OnDateTimeChanged(object sender, DateTimeEventArgs args)
        {            
            if (this.DateChanged != null)
            {
                this.DateChanged(this, args);
            }

            if (!args.Cancel && !args.DateTime.Equals(this.Date.Date))
            {
                this.Date = args.DateTime;
            }
        }

        /// <summary>
        /// Set the date to this control to display
        /// </summary>
        private void UpdateText()
        {
            if (this.Date == DateTime.MinValue)
            {
                base.Text = string.Empty;
            }
            else if (!string.IsNullOrWhiteSpace(this.Format))
            {
                base.Text = this.Date.ToString(this.Format);
            }
        }

        /// <summary>
        /// Clear data value by setting to DateTime.MinValue
        /// </summary>
        /// <param name="obj">The object calling clear</param>
        private void Clear(object obj)
        {
            this.Date = DateTime.MinValue;
        }
    }
}
